﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace MicroDb.Querys
{
    class JoinTable
    {
        public JoinTable(IQueryable source, JoinType joinType, Expression onPredicate)
        {
            Source = source;
            JoinType = joinType;
            OnPredicate = onPredicate;
        }

        public IQueryable Source { get; }

        public JoinType JoinType { get; }

        public Expression OnPredicate { get; }

    }


    class JoinTableObject
    {
        public IQueryable Source { get; set; }

        public List<JoinTable> Joins { get; set; }

        public List<Expression> WherePredicates { get; set; }

        public List<(Expression, OrderType)> OrderByPredicates { get; set; }


    }
    interface IDBJoinQueryable
    {
        List<JoinTable> ToJoinTableList();

        JoinTableObject Do();

    }
    abstract class DBJoinQueryable<T1> : IDBJoinQueryable
    {
        public DBJoinQueryable(IQueryable<T1> source, JoinType joinType)
        {
            this.Queryable = source;
            this.JoinType = joinType;
        }

        protected IQueryable<T1> Queryable { get; }

        protected JoinType JoinType { get; }

        protected Expression OnPredicate { get; set; }

        protected List<Expression> WherePredicates { get; set; }

        protected List<(Expression, OrderType)> OrderByPredicates { get; set; }

        public abstract List<JoinTable> ToJoinTableList();

        public IQueryable<T1> GetSource()
        {
            return Queryable;
        }

        public JoinTableObject Do()
        {
            return new JoinTableObject()
            {
                OrderByPredicates = OrderByPredicates,
                WherePredicates = WherePredicates,
                Source = GetSource(),
                Joins = ToJoinTableList()
            };
        }

    }

    class DBJoinQueryable<T1, T2> : DBJoinQueryable<T1>, IJoinStartQueryable<T1, T2>
    {
        public DBJoinQueryable(IQueryable<T1> source, IQueryable<T2> source2, JoinType joinType) : base(source, joinType)
        {
            this.NextQueryable = source2;
        }

        IQueryable<T2> NextQueryable { get; }


        public IJoinQueryable<T1, T2> Where(Expression<Func<T1, T2, bool>> predicate)
        {
            if (predicate == null) return this;
            if (WherePredicates == null) WherePredicates = new List<Expression>();
            WherePredicates.Add(predicate);
            return this;
        }

        public IJoinQueryable<T1, T2> OrderBy<TKey>(Expression<Func<T1, T2, TKey>> keySelector, OrderType orderType)
        {
            if (orderType == OrderType.None || keySelector == null) return this;
            if (OrderByPredicates == null) OrderByPredicates = new List<(Expression, OrderType)>();
            OrderByPredicates.Add((keySelector, orderType));
            return this;
        }


        public IJoinStartQueryable<T1, T2> On(Expression<Func<T1, T2, bool>> onPredicate)
        {
            OnPredicate = onPredicate;
            return this;
        }


        public IQueryable<TResult> Select<TResult>(Expression<Func<T1, T2, TResult>> selectSelector)
        {
            Expression[] arguments = new Expression[] { Expression.Quote(selectSelector) };
            var f = new Func<Expression<Func<T1, T2, TResult>>, IQueryable<TResult>>(Select<TResult>);
            return NextQueryable.Provider.CreateQuery<TResult>(Expression.Call(Expression.Constant(this), f.Method, arguments));
        }

        public IJoinStartQueryable<T1, T2, T3> Join<T3>(IQueryable<T3> source, JoinType joinType)
        {
            if (OnPredicate == null) throw Error.SystemError("Join 前缺少 ON 语句");
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            return new DBJoinQueryable<T1, T2, T3>(this, source, joinType);
        }

        public override List<JoinTable> ToJoinTableList()
        {
            return new List<JoinTable>() { new JoinTable(NextQueryable, JoinType, OnPredicate) };
        }


    }

    class DBJoinQueryable<T1, T2, T3> : DBJoinQueryable<T1>, IJoinStartQueryable<T1, T2, T3>
    {
        public DBJoinQueryable(DBJoinQueryable<T1, T2> source, IQueryable<T3> source2, JoinType joinType) : base(source.GetSource(), joinType)
        {
            this.SourceQueryable = source;
            this.NextQueryable = source2;
        }

        DBJoinQueryable<T1, T2> SourceQueryable { get; }
        IQueryable<T3> NextQueryable { get; }



        public IJoinStartQueryable<T1, T2, T3> On(Expression<Func<T1, T2, T3, bool>> onPredicate)
        {
            OnPredicate = onPredicate;
            return this;
        }
        public IJoinStartQueryable<T1, T2, T3> On(Expression<Func<T2, T3, bool>> onPredicate)
        {
            OnPredicate = onPredicate;
            return this;
        }
        public IJoinQueryable<T1, T2, T3> Where(Expression<Func<T1, T2, T3, bool>> predicate)
        {
            if (WherePredicates == null) WherePredicates = new List<Expression>();
            WherePredicates.Add(predicate);
            return this;
        }


        public IJoinStartQueryable<T1, T2, T3, T4> Join<T4>(IQueryable<T4> source, JoinType joinType)
        {
            if (OnPredicate == null) throw Error.SystemError("Join 前缺少 ON 语句");
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            return new DBJoinQueryable<T1, T2, T3, T4>(this, source, joinType);
        }

        /// <summary>
        /// 生成新的表 IQueryable
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="selectSelector"></param>
        /// <returns></returns>
        public IQueryable<TResult> Select<TResult>(Expression<Func<T1, T2, T3, TResult>> selectSelector)
        {
            Expression[] arguments = new Expression[] { Expression.Quote(selectSelector) };
            var f = new Func<Expression<Func<T1, T2, T3, TResult>>, IQueryable<TResult>>(Select);
            return NextQueryable.Provider.CreateQuery<TResult>(Expression.Call(Expression.Constant(this), f.Method, arguments));
        }

        public override List<JoinTable> ToJoinTableList()
        {
            var list = SourceQueryable.ToJoinTableList();
            list.Add(new JoinTable(NextQueryable, JoinType, OnPredicate));
            return list;
        }

        public IJoinQueryable<T1, T2, T3> OrderBy<TKey>(Expression<Func<T1, T2, T3, TKey>> keySelector, OrderType orderType)
        {
            if (orderType == OrderType.None || keySelector == null) return this;
            if (OrderByPredicates == null) OrderByPredicates = new List<(Expression, OrderType)>();
            OrderByPredicates.Add((keySelector, orderType));
            return this;
        }
    }
    class DBJoinQueryable<T1, T2, T3, T4> : DBJoinQueryable<T1>, IJoinStartQueryable<T1, T2, T3, T4>
    {
        public DBJoinQueryable(DBJoinQueryable<T1, T2, T3> source, IQueryable<T4> source2, JoinType joinType) : base(source.GetSource(), joinType)
        {
            this.SourceQueryable = source;
            this.NextQueryable = source2;
        }

        DBJoinQueryable<T1, T2, T3> SourceQueryable { get; }
        IQueryable<T4> NextQueryable { get; }


        public IJoinQueryable<T1, T2, T3, T4> Where(Expression<Func<T1, T2, T3, T4, bool>> predicate)
        {
            if (WherePredicates == null) WherePredicates = new List<Expression>();
            WherePredicates.Add(predicate);
            return this;
        }

        public IJoinStartQueryable<T1, T2, T3, T4> On(Expression<Func<T1, T2, T3, T4, bool>> onPredicate)
        {
            OnPredicate = onPredicate;
            return this;
        }
        public IJoinStartQueryable<T1, T2, T3, T4> On(Expression<Func<T3, T4, bool>> onPredicate)
        {
            OnPredicate = onPredicate;
            return this;
        }
        public IJoinStartQueryable<T1, T2, T3, T4, T5> Join<T5>(IQueryable<T5> source, JoinType joinType)
        {
            if (OnPredicate == null) throw Error.SystemError("Join 前缺少 ON 语句");
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            return new DBJoinQueryable<T1, T2, T3, T4, T5>(this, source, joinType);
        }


        /// <summary>
        /// 生成新的表 IQueryable
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="selectSelector"></param>
        /// <returns></returns>
        public IQueryable<TResult> Select<TResult>(Expression<Func<T1, T2, T3, T4, TResult>> selectSelector)
        {
            Expression[] arguments = new Expression[] { Expression.Quote(selectSelector) };
            var f = new Func<Expression<Func<T1, T2, T3, T4, TResult>>, IQueryable<TResult>>(Select);
            return NextQueryable.Provider.CreateQuery<TResult>(Expression.Call(Expression.Constant(this), f.Method, arguments));
        }

        public override List<JoinTable> ToJoinTableList()
        {
            var list = SourceQueryable.ToJoinTableList();
            list.Add(new JoinTable(NextQueryable, JoinType, OnPredicate));
            return list;
        }

        public IJoinQueryable<T1, T2, T3, T4> OrderBy<TKey>(Expression<Func<T1, T2, T3, T4, TKey>> keySelector, OrderType orderType)
        {
            if (orderType == OrderType.None || keySelector == null) return this;
            if (OrderByPredicates == null) OrderByPredicates = new List<(Expression, OrderType)>();
            OrderByPredicates.Add((keySelector, orderType));
            return this;
        }
    }

    class DBJoinQueryable<T1, T2, T3, T4, T5> : DBJoinQueryable<T1>, IJoinStartQueryable<T1, T2, T3, T4, T5>
    {
        public DBJoinQueryable(DBJoinQueryable<T1, T2, T3, T4> source, IQueryable<T5> source2, JoinType joinType) : base(source.GetSource(), joinType)
        {
            this.SourceQueryable = source;
            this.NextQueryable = source2;
        }


        DBJoinQueryable<T1, T2, T3, T4> SourceQueryable { get; }
        IQueryable<T5> NextQueryable { get; }

        public IJoinStartQueryable<T1, T2, T3, T4, T5> On(Expression<Func<T1, T2, T3, T4, T5, bool>> onPredicate)
        {
            OnPredicate = onPredicate;
            return this;
        }

        public IJoinStartQueryable<T1, T2, T3, T4, T5> On(Expression<Func<T4, T5, bool>> onPredicate)
        {
            OnPredicate = onPredicate;
            return this;
        }

        public IJoinQueryable<T1, T2, T3, T4, T5> Where(Expression<Func<T1, T2, T3, T4, T5, bool>> predicate)
        {
            if (WherePredicates == null) WherePredicates = new List<Expression>();
            WherePredicates.Add(predicate);
            return this;
        }

        /// <summary>
        /// 生成新的表 IQueryable
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="selectSelector"></param>
        /// <returns></returns>
        public IQueryable<TResult> Select<TResult>(Expression<Func<T1, T2, T3, T4, T5, TResult>> selectSelector)
        {
            Expression[] arguments = new Expression[] { Expression.Quote(selectSelector) };
            var f = new Func<Expression<Func<T1, T2, T3, T4, T5, TResult>>, IQueryable<TResult>>(Select);
            return NextQueryable.Provider.CreateQuery<TResult>(Expression.Call(Expression.Constant(this), f.Method, arguments));
        }




        public IJoinQueryable<T1, T2, T3, T4, T5> OrderBy<TKey>(Expression<Func<T1, T2, T3, T4, T5, TKey>> keySelector, OrderType orderType)
        {
            if (orderType == OrderType.None || keySelector == null) return this;
            if (OrderByPredicates == null) OrderByPredicates = new List<(Expression, OrderType)>();
            OrderByPredicates.Add((keySelector, orderType));
            return this;
        }

        public override List<JoinTable> ToJoinTableList()
        {
            var list = SourceQueryable.ToJoinTableList();
            list.Add(new JoinTable(NextQueryable, JoinType, OnPredicate));
            return list;
        }

    }

}
